/*
*  Impp - Instant Messenger
*
*  Copyright (C) 2008  Oliver Schneider
*
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "Oscar.h"
#include "OscarXStatus.h"

#include <iostream>
#include <string>
#include <sstream>

#include "packages/SrvIcqError.h"

#include "packages/authorization/CliHelloPackage.h"
#include "packages/authorization/CliUinPackage.h"
#include "packages/authorization/SrvAuthKeyResponse.h"
#include "packages/authorization/CliMd5Login.h"
#include "packages/authorization/SrvLoginReplay.h"
#include "packages/authorization/CliCookie.h"

#include "packages/service/CliFamiliesVersions.h"
#include "packages/service/CliRatesRequest.h"
#include "packages/service/SrvRateLimitInfo.h"
#include "packages/service/SrvRedirectXService.h"
#include "packages/service/SrvExtStatus.h"

#include "packages/service/CliRatesAck.h"
#include "packages/service/CliReady.h"
#include "packages/service/CliSetxStatus.h"
#include "packages/service/CliReqSelfinfo.h"
#include "packages/service/CliServiceXReq.h"

#include "packages/ssi/SrvSsiXModXAck.h"
#include "packages/ssi/SrvSsiReplay.h"
#include "packages/ssi/SrvSsiAuthXRequest.h"
#include "packages/ssi/CliSsiRightsRequest.h"
#include "packages/ssi/CliSsiCheckout.h"
#include "packages/ssi/CliSsiAuthXReply.h"
#include "packages/ssi/CliSsiActivate.h"
#include "packages/ssi/CliSsiXUpdate.h"
#include "packages/ssi/CliSsiXAdd.h"
#include "packages/ssi/CliSsiXDelete.h"
#include "packages/ssi/CliSsiEditBegin.h"
#include "packages/ssi/CliSsiEditEnd.h"

#include "packages/location/CliLocationRigthsReq.h"
#include "packages/location/CliSetLocationInfo.h"
#include "packages/location/CliLocationInfoReq.h"
#include "packages/location/SrvUserXOnlineXInfo.h"

#include "packages/buddy/CliBuddyListRightsReq.h"
#include "packages/buddy/SrvUserOnline.h"
#include "packages/buddy/SrvUserOffline.h"

#include "packages/msg/CliSetIcbmParms.h"
#include "packages/msg/CliIcbmParamReq.h"
#include "packages/msg/SrvClientIcbm.h"
#include "packages/msg/SrvClientIcbmSendxAck.h"
#include "packages/msg/CliSendIcbmCh1.h"
#include "packages/msg/SrvMsgAck.h"
#include "packages/msg/SrvMissedMessage.h"
#include "packages/msg/SrvTypingNotification.h"
#include "packages/msg/CliTypingNotification.h"
#include "packages/msg/CliIcbmSendxAck.h"
#include "packages/msg/SrvIcbmError.h"
#include "packages/msg/CliSomeMsgRequest.h"
#include "packages/msg/CliSendIcbmCh2FileAccept.h"

#include "packages/bos/CliPrivacyRightsReq.h"
#include "packages/bos/SrvPrivacyRightsReply.h"

#include "packages/extension/CliDeleteOfflineMsgsReq.h"
#include "packages/extension/CliOfflineMessageReq.h"
#include "packages/extension/CliShortinfoRequest.h"
#include "packages/extension/CliFullInfoRequest2.h"
#include "packages/extension/SrvMetaData.h"
#include "packages/extension/CliMetaSetStatusMessage.h"

#include "packages/avatar/SrvIcqIconReplay.h"
#include "packages/avatar/CliIconUpload.h"
#include "packages/avatar/SrvIconUploadAck.h"

#include "packages/close/SrvCloseConnection.h"

#include "tasks/ShortUserInfoTask.h"
#include "tasks/FullUserInfoTask.h"
#include "tasks/MessageSendTask.h"
#include "tasks/ServiceRequestTask.h"
#include "tasks/AvatarRequestTask.h"
#include "tasks/AwayMessageRequestTask.h"
#include "tasks/SsiUpdateTask.h"
#include "tasks/CheckStatusTask.h"
#include "tasks/SetOwnAvatarTask.h"
#include "tasks/SetStatusTask.h"
#include "tasks/ConnectionTask.h"
#include "tasks/OscarFileTransfareTask.h"

#include "../../core/Impp.h"

#include <wx/wfstream.h>

namespace Impp {

IMPLEMENT_DYNAMIC_CLASS(Oscar, iProtocol)

Oscar::Oscar():
	iProtocol(),
	taskManager(new TaskManager(this)),
	login(new OscarServerCon(this)),
	bos(new OscarServerCon(this)),
	service(new OscarServerCon(this)),
	ssi(new SsiPool()),
	account(NULL),
	state(OS_DISCONNECTED),
	loginState(LOGIN_STATE_NOTINITIALIZED),
	statusToSet(OFFLINE),
	visibilityToSet(PRICACY_BLOCK_ONLY_DENYLIST),
	metaSeqNr(0),
	serviceConRequested(false)

{
	login->setTaskManager(taskManager);
	bos->setTaskManager(taskManager);
	service->setTaskManager(taskManager);

    Connect(EVT_PACKAGE,PackageEventHandler(Oscar::onPackageIn));
    Connect(EVT_CONNECTION,ConnectionEventHandler(Oscar::onConnectionEvent));
}

Oscar::~Oscar()
{
	delete login;
	delete bos;
	delete service;
	delete taskManager;
	delete ssi;
}

void Oscar::init(Account* account){
	this->account=account;
}

void Oscar::connect(const ActionId & id, const Status & status, const wxString & statusMsg, const PrivacySetting & visibility){

	if(state==OS_DISCONNECTED){
		state=OS_CONNECTING_TO_LOGIN;

		visibilityToSet = visibility;
		statusToSet=status;

		taskManager->addTask(new ConnectionTask(this, id));

		login->connect(wxT("login.icq.com"),5190);
		//login->connect(wxT("localhost"),5190);

	}else{
		wxLogDebug(wxT("Oscar::connect: Oscar is not disconnected"));
	}

}

void Oscar::sendMessage(ActionId id, Buddy* & buddy,const wxString & msg){
	if(state==OS_CONNECTED){
		qword cookie = createCookie();
		bos->sendPackage(DATA, new CliSendIcbmCh1(buddy,msg,cookie,bos->nextRequestId()), PRIORITY_HIGHEST,TASK_HANDLE_COOKIE,cookie);
		taskManager->addTask(new MessageSendTask(this,buddy,cookie,id));
	}else{
		wxLogDebug(wxT("Oscar::sendMessage: can not send, not connected"));
	}
}

void Oscar::sendTypingNotification(Buddy* buddy, TypingState state){
	if(this->state==OS_CONNECTED){
		bos->sendPackage(DATA, new CliTypingNotification(buddy,state), PRIORITY_HIGHEST,TASK_HANDLE_NOHANDLE,0x00);
	}else{
		wxLogDebug(wxT("Oscar::sendTypingNotification: can not send, not connected"));
	}
}

void Oscar::changeCustomName(Buddy* buddy, const wxString & newCustomName){
	if(state==OS_CONNECTED){
		dword requestId = bos->nextRequestId();
		taskManager->addTask(new SsiUpdateTask(this,buddy,requestId,SSI_UPDATE_RENAME_BUDDY));
		bos->sendPackage(DATA,new CliSsiXUpdate(requestId, this, buddy,newCustomName),PRIORITY_HIGHT,TASK_HANDLE_REQUESTID,requestId);
	}else{
		wxLogDebug(wxT("Oscar::changeCustomName: can not change, not connected"));
	}
}

void Oscar::changeBuddyGroup(Buddy* buddy,Group* newGroup){
	if(state==OS_CONNECTED){
		dword requestId = bos->nextRequestId();
		bos->sendPackage(DATA,new CliSsiEditBegin(bos->nextRequestId()),PRIORITY_HIGHT);
		taskManager->addTask(new SsiUpdateTask(this,buddy,requestId,SSI_UPDATE_MOVE_BUDDY));
		bos->sendPackage(DATA,new CliSsiXDelete(requestId, this, buddy, false),PRIORITY_HIGHT,TASK_HANDLE_REQUESTID,requestId);
		bos->sendPackage(DATA,new CliSsiXAdd(bos->nextRequestId(), this, buddy, newGroup),PRIORITY_HIGHT,TASK_HANDLE_REQUESTID,requestId);
		bos->sendPackage(DATA,new CliSsiEditEnd(bos->nextRequestId()),PRIORITY_HIGHT);
	}else{
		wxLogDebug(wxT("Oscar::changeBuddyGroup: can not change, not connected"));
	}
}

void Oscar::changeAddToPrivacyList(Buddy* buddy, const int & privacyList){
	if(state==OS_CONNECTED){
		//send Ssi Start so that server now that we want to edit ssi data
		bos->sendPackage(DATA,new CliSsiEditBegin(bos->nextRequestId()),PRIORITY_HIGHT);
		//now Insert the SsiUpdateTask and send the SsiAdd Package:
		dword requestId = bos->nextRequestId();
		taskManager->addTask(new SsiUpdateTask(this,buddy,requestId,SSI_UPDATE_ADD_TO_PRIVACY_LIST,privacyList));
		bos->sendPackage(DATA,new CliSsiXAdd(requestId, this, buddy, privacyList),PRIORITY_HIGHT,TASK_HANDLE_REQUESTID,requestId);
		//send that we finished editing Ssi
		bos->sendPackage(DATA,new CliSsiEditEnd(bos->nextRequestId()),PRIORITY_HIGHT);
	}else{
		wxLogDebug(wxT("Oscar::changeAddToLists: can not change, not connected"));
	}
}

void Oscar::changeRemoveFromPrivacyList(Buddy* buddy, const int & privacyList){
	if(state==OS_CONNECTED){
		//send Ssi Start so that server now that we want to edit ssi data
		bos->sendPackage(DATA,new CliSsiEditBegin(bos->nextRequestId()),PRIORITY_HIGHT);
		//now Insert the SsiUpdateTask and send the SsiRemove Package:
		dword requestId = bos->nextRequestId();
		taskManager->addTask(new SsiUpdateTask(this,buddy,requestId,SSI_UPDATE_REMOVE_FROM_PRIVACY_LIST,privacyList));
		bos->sendPackage(DATA,new CliSsiXDelete(requestId, this, buddy, privacyList),PRIORITY_HIGHT,TASK_HANDLE_REQUESTID,requestId);
		//send that we finished editing Ssi
		bos->sendPackage(DATA,new CliSsiEditEnd(bos->nextRequestId()),PRIORITY_HIGHT);
	}else{
		wxLogDebug(wxT("Oscar::changeRemoveFromLists: can not change, not connected"));
	}
}

void Oscar::changeOwnAvatar(const wxString & avatarFile){
	if(state == OS_CONNECTED){
		taskManager->addTask(new SetOwnAvatarTask(this,0x0,avatarFile));
	}else{
		wxLogDebug(wxT("Oscar::changeOwnAvatar: coud not send request,  not connected"));
	}
}

ConnectionState Oscar::getConnectionState(){
	switch (state) {
		case OS_CONNECTED:
		case OS_DISCONNECTION:
			return CONNECTION_STATE_CONNECTED;
			break;
		case OS_DISCONNECTED:
			return CONNECTION_STATE_DISCONNECTED;
		case OS_CONNECTING_TO_BOS:
		case OS_CONNECTING_TO_LOGIN:
			return CONNECTION_STATE_CONNECTING;
		default:
			return CONNECTION_STATE_DISCONNECTED;
			break;
	}
}

void Oscar::setStatus(const ActionId & id, const Status & status, const wxString & statusMsg){
	statusToSet=status;

	if(state==OS_CONNECTED || loginState == LOGIN_STATE_SETTING_STATUS_AND_PRIVACY){
		if(status!=OFFLINE){
			dword requestId = bos->nextRequestId();

			taskManager->addTask(new SetStatusTask(id, requestId, status, this));

			bos->sendPackage(DATA,new CliSetLocationInfo(),PRIORITY_HIGHT);
			bos->sendPackage(DATA,new CliMetaSetStatusMessage(bos->nextRequestId(),metaSeqNr++, statusMsg, this),PRIORITY_HIGHT);
			bos->sendPackage(DATA,new CliSetxStatus(status,bos->nextRequestId()),PRIORITY_HIGHT);
			bos->sendPackage(DATA,new CliSetLocationInfo(bos->nextRequestId(),statusMsg),PRIORITY_HIGHT);
			bos->sendPackage(DATA,new CliSetxStatus(status, statusMsg, requestId,(ExtraStatus) status.getExtraStatus()),PRIORITY_HIGHT, TASK_HANDLE_REQUESTID, requestId);

			//the notification that the status was set too impp is the job of the Task

		}else{
			disconnect();
		}
	}else{
		wxLogDebug(wxT("Oscar::setStatus can not set Status, not connected"));
	}
}

void Oscar::setPrivacySettings(const PrivacySetting & visibility){
	visibilityToSet = visibility;

	if(state==OS_CONNECTED || loginState == LOGIN_STATE_SETTING_STATUS_AND_PRIVACY){
		if(visibility != ssi->getPrivacySetting()){
			bos->sendPackage(DATA,new CliSsiXUpdate(bos->nextRequestId(),this,visibility),PRIORITY_HIGHEST);
		}

	}else{
		wxLogDebug(wxT("Oscar::setPrivacySettings not connected"));
	}
}

void Oscar::IncomingFileTransfareRequestReceived(Buddy* buddy, qword cookie, const wxString & fileName, word fileCount, dword totalSize,const FileTransfareInfo & info){
	ActionId actionId = Impp::exemplar()->processIncomingFileTransfare(buddy,fileName,fileCount, totalSize);
	//taskManager->addTask(new OscarFileTransfareTask(this, buddy, cookie, actionId, fileName, fileCount, totalSize, info));
}

void Oscar::replayFileTransfare(const ActionId & id, Buddy* buddy, bool accept){

		std::vector<Task*> tasks;
		taskManager->getTasks(TASK_FILE_RECEIVE, tasks);
		OscarFileTransfareTask* task = NULL;

		std::vector<Task*>::iterator dIter(tasks.begin());
		for (; dIter != tasks.end(); ++dIter) {
			OscarFileTransfareTask* t = dynamic_cast<OscarFileTransfareTask*>(*dIter);
			wxASSERT(t != NULL);
			if(t->getActionId() == id){
				task = t;
				break;
			}
		}
		if(task != NULL){
			bos->sendPackage(DATA, new CliSendIcbmCh2FileAccept(buddy,task->getCookie(),bos->nextRequestId(),accept),PRIORITY_HIGHEST);
		}else{
			wxLogError(wxT("Oscar::replayFileTransfare: Task not found!"));
		}
}

void Oscar::disconnect(){
	state=OS_DISCONNECTION;
	ActionId id = -1;

	if(login->isConnected()){
		login->disconnect();
	}
	if(bos->isConnected()){
		bos->disconnect();
	}

	ConnectionTask* conTask = dynamic_cast<ConnectionTask*>(taskManager->getTask(TASK_SERVER_CONNECTION));
	if(conTask){
		id = conTask->getActionId();
		taskManager->deleteTask(conTask);
	}

	ssi->reset();
	loginState=LOGIN_STATE_NOTINITIALIZED;
	state=OS_DISCONNECTED;

	Impp::exemplar()->processConnectionLost(account->id, id, wxEmptyString);
}
bool Oscar::serviceConnectionRequested(){
	return serviceConRequested;
}

OscarServerCon* Oscar::getServiceConnection(){
	return service;
}

OscarServerCon* Oscar::getBosConnection(){
	return bos;
}

void Oscar::requestServiceConnection(){
	if(!serviceConRequested && !service->isConnected()){
		bos->sendPackage(DATA,new CliServiceXReq(ICQ_AVATAR_FAMILY, bos->nextRequestId()));
		taskManager->addTask(new ServiceRequestTask(this));
		serviceConRequested=true;
	}else{
		wxLogDebug(wxT("Oscar::requestServiceConnection: Request already sent or connected"));
	}

}

SsiPool* Oscar::getSsi(){
	return ssi;
}

qword Oscar::createCookie(){
	return (qword) ::wxGetLocalTime();
}

Account* Oscar::getAccount(){
	return account;
}

PrivacySetting Oscar::getPrivacySettings(){
	return visibilityToSet;
}

Status Oscar::getSupportedStatusMask(){
	return ONLINE|FREE4CHAT|AWAY|OCCUPIED|NA|DND|INVISIBLE|OFFLINE;
}
int Oscar::getExtraStatusCount(){
	return 24;
}

wxString Oscar::getExtraStatusName(int nr){
	return extraStatusToString(nr);
}

void Oscar::requestAvatar(const ActionId & id, Buddy* buddy){
	wxASSERT(buddy != NULL);
	taskManager->addTask(new AvatarRequestTask(this, buddy, id));
}

void Oscar::replayAuthorisation(Buddy* buddy, bool accept,const wxString & reason){
	wxASSERT(buddy != NULL);
	if(state==OS_CONNECTED){
		bos->sendPackage(DATA,new CliSsiAuthXReply(buddy,accept,reason,bos->nextRequestId()));
	}else{
		wxLogDebug(wxT("Oscar::replayAuthorisation not connected!"));
	}
}

void Oscar::checkUserStatus(Buddy* buddy){
	wxASSERT(buddy != NULL);
	if(state==OS_CONNECTED){
		dword reqId = bos->nextRequestId();
		taskManager->addTask(new CheckStatusTask(this,buddy,reqId));
		bos->sendPackage(DATA,new CliLocationInfoReq(buddy, GENERAL_INFO, reqId),PRIORITY_HIGHT,TASK_HANDLE_REQUESTID, reqId);
	}else{
		wxLogDebug(wxT("Oscar::checkUserStatus not connected!"));
	}
}

void Oscar::requestStatusMessage(Buddy* buddy){
	wxASSERT(buddy != NULL);
	if(state==OS_CONNECTED){
		dword reqId = bos->nextRequestId();
		bos->sendPackage(DATA,new CliLocationInfoReq(buddy,AWAY_MESSAGE, reqId),PRIORITY_LOW,TASK_HANDLE_REQUESTID, reqId);
		taskManager->addTask(new AwayMessageRequestTask(this,buddy,reqId));
	}else{
		wxLogDebug(wxT("Oscar::requestStatusMessage: can not request Status Message when not online"));
	}
}

void Oscar::statusMessageRequestReceived(Buddy* buddy, byte msgType, qword cookie){
	wxASSERT(buddy != NULL);

	if(Impp::exemplar()->getSelf(account->id)->getStatus()!=INVISIBLE || buddy->isOnPrivacyList(BUDDY_PRIVACY_VISIBLELIST)){
		wxLogDebug(wxT("Oscar::statusMessageRequestReceived: Sending Status Message to: %s"),buddy->getName().c_str());
		bos->sendPackage(DATA,new CliIcbmSendxAck(buddy,msgType,cookie,Impp::exemplar()->getSelf(account->id)->getStatusMsg(),bos->nextRequestId()));
	}else{
		wxLogDebug(wxT("Oscar::statusMessageRequestReceived: Not sending status msg because ivisible"));
	}



}

void Oscar::serviceRequestTimedOut(){
	serviceConRequested=false;
}

void Oscar::onPackageIn(PackageEvent & event){
	wxLogTrace(wxT("OSCAR_PACKAGE"), wxT("Package in: "));

	Flap* flap = event.getFlap();
	OscarServerCon* src = event.getSource();
	if(src==login){
		parseLoginPackage(flap);
	}else if (src==bos){
		parseBosPackage(flap);
	}else if (src==service){
		parseServicePackage(flap);
	}else{
		wxLogDebug(wxT("Oscar::onPackageIn: Error: Package from Unknown src"));
	}
	delete flap;
}

void Oscar::parseLoginPackage(Flap* flap2){
	switch (loginState) {
		case LOGIN_STATE_WAITING_FOR_LOGIN_HELLO:
		{
			//<< Hello from Server
			byte* data = flap2->getData();
			//Hello Package vom Server: 00 00 00 01
			if(flap2->getDataSize() != 4 || data[0]!=0x00 || data[1]!=0x00 || data[2]!=0x00 || data[3]!=0x01){
				wxLogError(wxT("Oscar::parseLoginPackage: ICQ Servers Hello Package was wrong!"));
				loginState=LOGIN_STATE_ERROR;
				disconnect();
				return;
			}

			//>> Client sends md5-authkey request
			login->sendPackage(LOGIN,new CliHelloPackage());
			wxLogTrace(wxT("OSCAR_PACKAGE"), wxT("Oscar::parseLoginPackage: CliHelloPackage sent"));

			wxString uin = Impp::exemplar()->getSelf(account->id)->getScreenName();
			//>> Client sends authorization request
			login->sendPackage(DATA,new CliUinPackage(uin));
			wxLogTrace(wxT("OSCAR_PACKAGE"), wxT("Oscar::parseLoginPackage: CliUinPackage sent"));

			loginState=LOGIN_STATE_WAITING_FOR_PW_HASH;
		}
		break;
		case LOGIN_STATE_WAITING_FOR_PW_HASH:
		case LOGIN_STATE_WAITING_FOR_PW_REPLAY:
		case LOGIN_STATE_WAITING_FOR_BOS_HELLO:
		case LOGIN_STATE_ERROR:
			{
				Flap& flap = *flap2;


				switch (flap.getHeader()->channel) {
					case LOGIN:
						wxLogDebug(wxT("Oscar::parseLoginPackage:  Data in Login channel ???"));
						break;
					case DATA:
						//Has an Header:
					{
						SrvSnacPackage  pack = SrvSnacPackage(flap);
						Snac snac = pack.header;


						wxLogTrace(wxT("OSCAR_PACKAGE_FINE"),wxT("Oscar::parseLoginPackage: Snac Header: family(%X),subtype(%X),flags(%X)"),snac.family,snac.subtype,snac.flags);

						switch (snac.family) {
							case ICQ_AUTHORIZATION_FAMILY:
								switch (snac.subtype) {
									case ICQ_SIGNON_AUTH_KEY:
										{
											SrvAuthKeyResponse srvAuthRes(flap);

											wxString uin = account->screenname;
											wxString pw = account->password;

											if(uin.IsEmpty()){
												wxLogError(wxT("Oscar::parseLoginPackage: Error: Screenname in Database is empty"));
											}
											if(pw.IsEmpty()){
												wxLogError(wxT("Oscar::parseLoginPackage: Error: Password in Database is empty"));
											}

											//>> Server sends authorization reply
											login->sendPackage(DATA,new CliMd5Login(srvAuthRes.authKey,uin,pw));
											wxLogTrace(wxT("OSCAR_PACKAGE"), wxT("CliMd5Login sent"));

											loginState=LOGIN_STATE_WAITING_FOR_PW_REPLAY;
										}
										break;
									case ICQ_SIGNON_LOGIN_REPLY:
									{
										SrvLoginReplay srvLoginR(flap);

														if(srvLoginR.error!=0){
															std::cout << "Login Error"<< std::endl;

															wxString err;
															err << srvLoginR.errorCode << wxT(": ") << SrvLoginReplay::errorCodeToString(srvLoginR.errorCode);

															wxLogError(err);

															loginState=LOGIN_STATE_ERROR;
															disconnect();
															return;
														}

														if(!srvLoginR.cookieLng){
															wxLogDebug(wxT("OSCAR"), wxT("Oscar::parseLoginPackage: Login Cookie is Empty"));
															loginState=LOGIN_STATE_ERROR;
															disconnect();
															return;
														}

														if(!srvLoginR.serverAddress){
															wxLogDebug(wxT("OSCAR"), wxT("Oscar::parseLoginPackage: ServerAddress is Empty"));
															loginState=LOGIN_STATE_ERROR;
															disconnect();
															return;
														}

														ConnectionTask* conTask = dynamic_cast<ConnectionTask*>(taskManager->getTask(TASK_SERVER_CONNECTION));

														if(conTask){
															LoginCookie* cookie = conTask->getCookiePointer();
															cookie->setData(srvLoginR.cookie, srvLoginR.cookieLng);

														}else{
															wxLogError(wxT("Oscar::parseLoginPackage: No Connection Task!"));
															disconnect();
														}

														IpAndPort ip(srvLoginR.serverAddress);

														login->disconnect();
														loginState=LOGIN_STATE_WAITING_FOR_BOS_HELLO;
														state=OS_CONNECTING_TO_BOS;

														wxLogTrace(wxT("OSCAR"), wxT("Oscar::parseLoginPackage: Connection complete"));

														bos->connect(ip.ip,ip.port);
									}
									break;
									default:
										wxLogDebug(wxT("Oscar::parseLoginPackage: Unhandled Package in ICQ_AUTHORIZATION_FAMILY"));
								}
							break;
							default:
								wxLogDebug(wxT("Oscar::parseLoginPackage: Unhandled Package in unhandled Family"));
								break;
						}

					}
					break;
					default:
						wxLogDebug(wxT("Oscar::parseLoginPackage: Unhandled Package in unhandeld Channel"));
						break;
				}
			}
		break;
		default:
			wxLogDebug(wxT("Oscar::parseLoginPackage: Unhandled Package, dont expected in this state"));
			break;
	}

}


void Oscar::parseBosPackage(Flap* flap2){
	switch (loginState) {
		case LOGIN_STATE_WAITING_FOR_BOS_HELLO:
		{

			//wxLogDebug(wxT("boslogin package gettet"));

			byte* data = flap2->getData();
			//Hello Package vom Server: 00 00 00 01
			if(flap2->getDataSize() != 4 || data[0]!=0x00 || data[1]!=0x00 || data[2]!=0x00 || data[3]!=0x01){
				wxLogError(wxT("ICQ Servers Hello Package was wrong!"));
				disconnect();
				return;
			}
			//>>CliHello

			ConnectionTask* conTask = dynamic_cast<ConnectionTask*>(taskManager->getTask(TASK_SERVER_CONNECTION));

			if(conTask){
				LoginCookie* cookie = conTask->getCookiePointer();
				bos->sendPackage(LOGIN,new CliCookie(cookie->cookie,cookie->lng));
				loginState=LOGIN_STATE_COMPLETE;
			}else{
				wxLogError(wxT("Oscar::parseBosPackage: No Connection Task!"));
			}
		}
		break;
		case LOGIN_STATE_COMPLETE:
			{
				Flap& flap = *flap2;


				switch (flap.getHeader()->channel) {
					case LOGIN:
						std::cout << "OscarServerCon::Entry: Data in Login channel ???" << std::endl;
						break;
					case DATA:
						//Has an Header:
					{
						SrvSnacPackage  pack = SrvSnacPackage(flap);
						Snac snac = pack.header;


						wxLogTrace(wxT("OSCAR_PACKAGE_FINE"),wxT("Oscar::parseBosPackage: Snach Header: family(%X),subtype(%X),flags(%X)"),snac.family,snac.subtype,snac.flags);

						switch (snac.family) {
							case ICQ_SERVICE_FAMILY:
								switch (snac.subtype) {
									case ICQ_SERVER_READY:
									{
										//TODO: parse 0001 and subtype: 0003
										bos->sendPackage(DATA, new CliFamiliesVersions());
									}
									break;
									case ICQ_SERVER_REDIRECT_SERVICE:
									{
										//read package info
										SrvRedirectXService s(flap);

										//get task
										ServiceRequestTask* srt = dynamic_cast<ServiceRequestTask*>(taskManager->getTask(TASK_SERVICE_CONNECTION_REQUEST));

										//task exsist
										if(srt){
											if(!s.server.IsEmpty() && s.cookie){

												IpAndPort ip(s.server);

												LoginCookie* loginCookie = srt->getCookiePointer();
												loginCookie->setData(s.cookie,s.cookie_lng);

												service->connect(ip.ip,ip.port);

											}else{
												wxLogError(wxT("Coudn't connect to Service Server, Addr. or cookie is empty"));
												srt->failed();
												taskManager->deleteTask(srt);

											}

										}else{
											wxLogDebug(wxT("There was no service task!!??"));
										}



									}
									break;
									case ICQ_SERVER_FAMILIES2:
									{
										//TODO: parse 0001 and subtype: 0018
										bos->sendPackage(DATA, new CliRatesRequest());
									}
									break;
									case ICQ_SERVER_MOTD:
										//SrvMotd motd(flap);
										//Message of the Day can be ignored
										break;
									case ICQ_SERVER_RATE_INFO:
									{
										SrvRateLimitInfo info(flap);
										bos->setRates(info.rates,info.rgs);

										if(!snac.flags){
											bos->sendPackage(DATA, new CliRatesAck(info.rgs));
											bos->sendPackage(DATA, new CliReqSelfinfo(bos->nextRequestId()));
											bos->sendPackage(DATA, new CliSsiRightsRequest());
											bos->sendPackage(DATA, new CliSsiCheckout(bos->nextRequestId()));
											bos->sendPackage(DATA, new CliLocationRigthsReq());
											bos->sendPackage(DATA, new CliBuddyListRightsReq());
											bos->sendPackage(DATA, new CliIcbmParamReq());
											bos->sendPackage(DATA, new CliPrivacyRightsReq());
											bos->sendPackage(DATA, new CliSetxStatus(bos->nextRequestId()));	//dc info
										}else{
											wxLogTrace(wxT("OSCAR_PACKAGE"),wxT("Oscar::parseBosPackage: SrvRateLimitInfo snac flag was set "));
										}

									}
									break;
									case ICQ_SERVER_EXTSTATUS:
									{
										SrvExtStatus extStatus(flap, this);
									}
									break;
									default:
										wxLogDebug(wxT("Oscar::parseBosPackage: Unhandled Package in ICQ_SERVICE_FAMILY"));
										break;
								}
								break;
							case ICQ_LOCATION_FAMILY:
								switch (snac.subtype) {
									case ICQ_LOCATION_USR_INFO_REPLY:
									{
										SrvUserXOnlineXInfo userxinfo(this,flap);
										break;
									}
									case ICQ_LOCATION_RIGHTS_REPLY:
										//TODO: parse 0002 and subtype: 0003
									default:
										wxLogDebug(wxT("Oscar::parseBosPackage: Unhandled Package in ICQ_LOCATION_FAMILY"));
								}
								break;
							case ICQ_BUDDY_FAMILY:
								switch (snac.subtype) {
									case ICQ_USER_ONLINE:
									{
										SrvUserOnline online(flap, this);
									}
									break;
									case ICQ_USER_OFFLINE:
									{
										SrvUserOffline offline(flap, this);
									}
									break;
									default:
										wxLogDebug(wxT("Oscar::parseBosPackage: Unhandled Package in ICQ_BUDDY_FAMILY"));
								}
								break;
							case ICQ_MSG_FAMILY:
								switch (snac.subtype) {
									case ICQ_MSG_SRV_ERROR:
									{
										SrvIcbmError error(this, flap);
									}
									break;
									case ICQ_MSG_SRV_RECV:
									{
										SrvClientIcbm icbm(this, flap);
									}
									break;
									case ICQ_MSG_RESPONSE:
									{
										SrvClientIcbmSendxAck cisxa(this, flap);
									}
									break;
									case ICQ_MSG_SRV_ACK:
									{
										SrvMsgAck sma(this, flap);
									}
									break;
									case ICQ_MSG_SRV_MISSED_MESSAGE:
									{
										SrvMissedMessage mm(flap, this);
									}
									break;
									case ICQ_MSG_MTN:
									{
										SrvTypingNotification tn(flap, this);
									}
									break;
									default:
										wxLogDebug(wxT("Oscar::parseBosPackage: Unhandled Package in ICQ_MSG_FAMILY"));
								}
								break;
							case ICQ_BOS_FAMILY:
								switch(snac.subtype){
									case ICQ_PRIVACY_RIGHTS_REPLY:
									{
										SrvPrivacyRightsReply prr(flap);
									}
									break;
									default:
										wxLogDebug(wxT("Oscar::parseBosPackage: Unhandled Package in ICQ_BOS_FAMILY"));
									break;
								}
								break;
							case ICQ_LOOKUP_FAMILY:
								wxLogDebug(wxT("Oscar::parseBosPackage: Unhandled Package in ICQ_LOOKUP_FAMILY"));
								break;
							case ICQ_STATS_FAMILY:
								wxLogDebug(wxT("Oscar::parseBosPackage: Unhandled Package in ICQ_STATS_FAMILY"));
								break;
							case ICQ_AVATAR_FAMILY:
								wxLogDebug(wxT("Oscar::parseBosPackage: Unhandled Package in ICQ_AVATAR_FAMILY"));
								break;
							case ICQ_LISTS_FAMILY:
								switch (snac.subtype) {
									/*
									 * type 0x06 = Server contact list reply
									 */
									case ICQ_LISTS_LIST:
									{
										SrvSsiReplay ssiReplay(flap, this);
										if(!snac.flags){ //It coud be that there are more Packages then flag will be > 0 and the last package will be flag = 0
												bos->sendPackage(DATA, new CliSsiActivate(),PRIORITY_HIGHT);
												bos->sendPackage(DATA, new CliSetLocationInfo(),PRIORITY_HIGHT);
												bos->sendPackage(DATA, new CliSetIcbmParms(),PRIORITY_HIGHT);
												//comment: because package already sent
												//bos->sendPackage(DATA, new CliReqSelfinfo(bos->nextRequestId()),PRIORITY_HIGHT);

												loginState=LOGIN_STATE_SETTING_STATUS_AND_PRIVACY;
												setPrivacySettings(visibilityToSet);
												setStatus(-1, statusToSet,Impp::exemplar()->getSelf(account->id)->getStatusMsg());

												bos->sendPackage(DATA, new CliReady(),PRIORITY_HIGHT);
												bos->sendPackage(DATA, new CliSomeMsgRequest(bos->nextRequestId()),PRIORITY_HIGHT);

												wxLogTrace(wxT("OSCAR_PACKAGE"),wxT("Oscar::parseBosPackage: CliReady sent"));

												loginState=LOGIN_STATE_COMPLETE;
												state=OS_CONNECTED;


												ConnectionTask* conTask = dynamic_cast<ConnectionTask*>(taskManager->getTask(TASK_SERVER_CONNECTION));

												if(conTask){
													Impp::exemplar()->processConnectionEstablished(account->id, conTask->getActionId(), statusToSet);
													taskManager->deleteTask(conTask);
												}else{
													wxLogError(wxT("Oscar::parseBosPackage: No Connection Task!"));
												}

												bos->sendPackage(DATA,new CliOfflineMessageReq(bos->nextRequestId(),metaSeqNr++, this),PRIORITY_HIGHT);
												requestAvatar(-1, Impp::exemplar()->getSelf(account->id));

										}else{
											wxLogTrace(wxT("OSCAR_PACKAGE"),wxT("Oscar::parseBosPackage: SrvSsiReplay snac flag was set "));
										}
									}
									break;
									case ICQ_LISTS_AUTHREQUEST:
									{
										SrvSsiAuthXRequest ssiAuthRequest(flap, this);
									}
									break;
									case ICQ_LISTS_ACK:
									{
										SrvSsiXModXAck ssiAck(flap, this);
									}
									break;
									default:
										wxLogDebug(wxT("Oscar::parseBosPackage: Unhandled Package in ICQ_LISTS_FAMILY"));
								}
								break;
							case ICQ_EXTENSIONS_FAMILY:
								switch (snac.subtype) {
									case ICQ_META_SRV_REPLY:
										{
											SrvMetaData msg(flap,this);
											if(msg.type==0x42){
												bos->sendPackage(DATA,new CliDeleteOfflineMsgsReq(bos->nextRequestId(), this));
												requestShortUserInfo(Impp::exemplar()->getSelf(account->id));
											}
										}
										break;
									default:
										wxLogDebug(wxT("Oscar::parseBosPackage: Unhandled Package in ICQ_EXTENSIONS_FAMILY"));
										break;
								}
								break;
							case ICQ_AUTHORIZATION_FAMILY:
								wxLogDebug(wxT("Oscar::parseBosPackage: Unhandled Package in ICQ_AUTHORIZATION_FAMILY"));
								break;
							default:
								wxLogDebug(wxT("Oscar::parseLoginPackage: Unhandled Package in unhandled Family"));
								break;
						}

					}
					break;
					case CLOSE:
					{
						wxLogDebug(wxT("Oscar::parseBosPackage: Package in Close Channel"));
						SrvCloseConnection conClose(flap);
						wxString reason;
						reason << wxT("You were disconnected from the icq Server.\n");
						reason << conClose.getReason();
						wxLogError(reason);
						disconnect();
					}
					break;
					case ERROR2:
					{
						wxLogDebug(wxT("Oscar::parseBosPackage: Package in Error Channel"));
						disconnect();
					}
					break;
					default:
						wxLogDebug(wxT("Oscar::parseBosPackage: Unhandled Package in unhandeld Channel"));
						break;
				}
			}
		break;
		default:
			wxLogDebug(wxT("Oscar::parseBosPackage: Unhandled Package, dont expected in this state"));
			break;
	}
}

void Oscar::parseServicePackage(Flap* flap2){
		Flap& flap = *flap2;

		switch (flap.getHeader()->channel) {
			case LOGIN:
			{
				byte* data = flap2->getData();

				//Hello Package vom Server: 00 00 00 01
				if(!(flap2->getDataSize() >= 4) || data[0]!=0x00 || data[1]!=0x00 || data[2]!=0x00 || data[3]!=0x01){
					wxLogError(wxT("ICQ Servers Hello Package was wrong!"));
					return;
				}
				//>>CliHello
				ServiceRequestTask* srt = dynamic_cast<ServiceRequestTask*>(taskManager->getTask(TASK_SERVICE_CONNECTION_REQUEST));
				if(srt){
					LoginCookie* cookie = srt->getCookiePointer();
					service->sendPackage(LOGIN,new CliCookie(cookie->cookie,cookie->lng));
				}else{
					wxLogError(wxT("ServiceRequestTask not found!"));
				}


			}
			break;
			case DATA:
				//Has an Header:
			{
				SrvSnacPackage  pack = SrvSnacPackage(flap);
				Snac snac = pack.header;


				wxLogTrace(wxT("OSCAR_PACKAGE_FINE"),wxT("Oscar::parseServicePackage: Snach Header: family(%X),subtype(%X),flags(%X)"),snac.family,snac.subtype,snac.flags);

				if(snac.subtype == 0x01){
					SrvIcqError error(flap);
				}

				switch (snac.family) {
					case ICQ_SERVICE_FAMILY:
						switch (snac.subtype) {
							case ICQ_SERVER_READY:
							{
								//TODO: parse 0001 and subtype: 0003
								service->sendPackage(DATA, new CliFamiliesVersions());
							}
							break;
							case ICQ_SERVER_FAMILIES2:
							{
								//TODO: parse 0001 and subtype: 0018
								service->sendPackage(DATA, new CliRatesRequest());
							}
							break;
							case ICQ_SERVER_RATE_INFO:
							{
								SrvRateLimitInfo info(flap);

								if(!snac.flags){
									service->sendPackage(DATA, new CliRatesAck(info.rgs));
									service->sendPackage(DATA, new CliReady());

									if(ServiceRequestTask*  t = dynamic_cast<ServiceRequestTask*>(taskManager->getTask(TASK_HANDLE_SPECIAL,SPECIAL_HANDLE_SERVICEREQUEST))){
										taskManager->deleteTask(t);
										serviceConRequested=false;
									}else{
										wxLogTrace(wxT("OSCAR"),wxT("Oscar::parseServicePackage: ServiceTask not found"));
									}
								}else{
									wxLogTrace(wxT("OSCAR_PACKAGE"),wxT("Oscar::parseServicePackage: SrvRateLimitInfo snac flag was set "));
								}

							}
							break;
							default:
								wxLogDebug(wxT("Oscar::parseServicePackage: Unhandled Package in ICQ_SERVICE_FAMILY"));
								break;
						}
						break;
					case ICQ_LOCATION_FAMILY:
						switch (snac.subtype) {
							default:
								wxLogDebug(wxT("Oscar::parseServicePackage: Unhandled Package in ICQ_LOCATION_FAMILY"));
						}
						break;
					case ICQ_BUDDY_FAMILY:
						switch (snac.subtype) {
							default:
								wxLogDebug(wxT("Oscar::parseServicePackage: Unhandled Package in ICQ_BUDDY_FAMILY"));
						}
						break;
					case ICQ_MSG_FAMILY:
						switch (snac.subtype) {
							default:
								wxLogDebug(wxT("Oscar::parseServicePackage: Unhandled Package in ICQ_MSG_FAMILY"));
						}
						break;
					case ICQ_BOS_FAMILY:
						switch(snac.subtype){
							default:
								wxLogDebug(wxT("Oscar::parseServicePackage: Unhandled Package in ICQ_BOS_FAMILY"));
							break;
						}
						break;
					case ICQ_LOOKUP_FAMILY:
						wxLogDebug(wxT("Oscar::parseServicePackage: Unhandled Package in ICQ_LOOKUP_FAMILY"));
						break;
					case ICQ_STATS_FAMILY:
						wxLogDebug(wxT("Oscar::parseServicePackage: Unhandled Package in ICQ_STATS_FAMILY"));
						break;
					case ICQ_AVATAR_FAMILY:
						switch (snac.subtype) {
							case ICQ_AVATAR_GET_REPLY:
							{
									SrvIcqIconReplay replay(flap, this);
									if(AvatarRequestTask*  t = dynamic_cast<AvatarRequestTask*>(taskManager->getTask(TASK_HANDLE_REQUESTID,snac.requestId))){
										taskManager->deleteTask(t);
										if(replay.success){
											wxLogTrace(wxT("AVATAR"),wxT("Oscar::parseServicePackage: Avatar succesfull received"));
										}
									}else{
										wxLogTrace(wxT("OSCAR"),wxT("Oscar::parseServicePackage: AvatarRequestTask not found"));
									}
							}
								break;
							case ICQ_AVATAR_UPLOAD_ACK:
							{
								SrvIconUploadAck ack(flap, this);
							}
								break;
							default:
								wxLogDebug(wxT("Oscar::parseServicePackage: Unhandled Package in ICQ_AVATAR_FAMILY"));
								break;
						}
						break;
					case ICQ_LISTS_FAMILY:
						switch (snac.subtype) {
							default:
								wxLogDebug(wxT("Oscar::parseServicePackage: Unhandled Package in ICQ_LISTS_FAMILY"));
						}
						break;
					case ICQ_EXTENSIONS_FAMILY:
						switch (snac.subtype) {
							default:
								wxLogDebug(wxT("Oscar::parseServicePackage: Unhandled Package in ICQ_EXTENSIONS_FAMILY"));
								break;
						}
						break;
					case ICQ_AUTHORIZATION_FAMILY:
						wxLogDebug(wxT("Oscar::parseServicePackage: Unhandled Package in ICQ_AUTHORIZATION_FAMILY"));
						break;
					default:
						wxLogDebug(wxT("Oscar::parseServicePackage: Unhandled Package in unhandled Family"));
						break;
				}

			}
			break;
			default:
				wxLogDebug(wxT("Oscar::parseServicePackage: Unhandled Package in unhandeld Channel"));
				break;
		}
}

void Oscar::onConnectionEvent(ConnectionEvent & event){
	OscarServerCon* src = event.getSource();
	ConEvt type = event.getEvtType();

	if(src==login){
		if(type==CON_EVT_CONNECTED){
			loginState=LOGIN_STATE_WAITING_FOR_LOGIN_HELLO;
			wxLogTrace(wxT("OSCAR"),wxT("Oscar::onConnectionEvent: Login: connected"));
		}else if(type==CON_EVT_LOST){
			disconnect();
			wxLogTrace(wxT("OSCAR"),wxT("Oscar::onConnectionEvent: Login: connection Lost"));
		}
	}else if (src==bos){
		if(type==CON_EVT_CONNECTED){
			wxLogTrace(wxT("OSCAR"),wxT("Oscar::onConnectionEvent: bos: connected"));
		}else if(type==CON_EVT_LOST){
			disconnect();
			wxLogTrace(wxT("OSCAR"),wxT("Oscar::onConnectionEvent: bos: connection Lost"));
		}
	}else if (src==service){
		if(type==CON_EVT_CONNECTED){
			wxLogTrace(wxT("OSCAR"),wxT("Oscar::onConnectionEvent: service: connected"));
		}else if(type==CON_EVT_LOST){
			wxLogTrace(wxT("OSCAR"),wxT("Oscar::onConnectionEvent: service: connection Lost"));
		}
	}else{
		wxLogDebug(wxT("Oscar::onPackageIn: Error: Package from Unknown src" ));
	}
}

void Oscar::requestShortUserInfo(Buddy* buddy){
	if(state==OS_CONNECTED){
		word curSeqNr = metaSeqNr++;
		bos->sendPackage(DATA,new CliShortinfoRequest(buddy,bos->nextRequestId(),curSeqNr, this),PRIORITY_LOW,TASK_HANDLE_SEQNR,curSeqNr);
		taskManager->addTask(new ShortUserInfoTask(this,buddy,curSeqNr));
	}else{
		wxLogDebug(wxT("Oscar::requestShortUserInfo: can not request user info, not connected!"));
	}
}

void Oscar::requestFullUserInfo(Buddy* buddy){
	if(state==OS_CONNECTED){
		wxLogDebug(wxT("requesting full userinfo"));
		word curSeqNr = metaSeqNr++;
		bos->sendPackage(DATA,new CliFullInfoRequest2(buddy,bos->nextRequestId(),curSeqNr, this),PRIORITY_LOW,TASK_HANDLE_SEQNR,curSeqNr);
		taskManager->addTask(new FullUserInfoTask(this,buddy,curSeqNr));
	}else{
		wxLogDebug(wxT("Oscar::requestShortUserInfo: can not request user info, not connected!"));
	}
}

TaskManager* Oscar::getTaskManager(){
	return taskManager;
}


}

